/*
 *      AgendaEngine.java
 *      
 *      Copyright 2011 florent <florent@P5E3deluxe>
 *      
 *      This program is free software; you can redistribute it and/or modify
 *      it under the terms of the GNU General Public License as published by
 *      the Free Software Foundation; either version 2 of the License, or
 *      (at your option) any later version.
 *      
 *      This program is distributed in the hope that it will be useful,
 *      but WITHOUT ANY WARRANTY; without even the implied warranty of
 *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *      GNU General Public License for more details.
 *      
 *      You should have received a copy of the GNU General Public License
 *      along with this program; if not, write to the Free Software
 *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 *      MA 02110-1301, USA.
 *      
 *      
 */
/**
 * contrôle le stockage des événements
 */
package pda.agenda.datas;

import java.util.GregorianCalendar;
import java.util.Hashtable;
import java.util.Enumeration;
import java.util.ArrayList;

import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.io.NotSerializableException;
import java.io.FileNotFoundException;
import java.io.IOException;

public class AgendaEngine {
	
	private Hashtable<GregorianCalendar,Activity> listeDesActivites;
	
	/**
	 * Constructeur
	 */
	public AgendaEngine() {
		listeDesActivites = new Hashtable<GregorianCalendar,Activity>();
		charger();
	}
	
	/**
	 * Renvoie l'ensemble des clés contenues dans la collection
	 * @return liste des clés de l'agenda
	 */
	public Enumeration<GregorianCalendar> cles() {
		return listeDesActivites.keys();
	}
	
	/**
	 * methode pour ajouter des activités dans l'agenda
	 * @param donnees l'activité à ajouter
	 */
	public void ajouterUneActivite(Activity donnees) throws ChevauchementActiviteException {
		
		GregorianCalendar dateDebut1 = donnees.getDateDebut();
		GregorianCalendar dateFin1 = donnees.getDateFin();
		
		Enumeration it = listeDesActivites.keys();
		while(it.hasMoreElements()){
			Activity act = listeDesActivites.get(it.nextElement());
			GregorianCalendar dateDebut2 = act.getDateDebut();
			GregorianCalendar dateFin2 = act.getDateFin();
			// on regarde si la date de début n'est pas déjà dans la période d'une activité
			if( dateDebut2.getTimeInMillis() <= dateDebut1.getTimeInMillis() && dateDebut1.getTimeInMillis() < dateFin2.getTimeInMillis() ){
				throw new ChevauchementActiviteException( String.valueOf(act.getDateDebut().getTimeInMillis()) );
			}
			// on regarde la même chose avec la date de fin
			else if( dateDebut2.getTimeInMillis() < dateFin1.getTimeInMillis() && dateFin1.getTimeInMillis() <= dateFin2.getTimeInMillis() ){
				throw new ChevauchementActiviteException( String.valueOf(act.getDateDebut().getTimeInMillis()) );
			}
			// on regarde si on n'inclu pas d'activité dans une autre
			else if( dateDebut1.getTimeInMillis() < dateFin2.getTimeInMillis() && dateFin2.getTimeInMillis() < dateFin1.getTimeInMillis() ){
				throw new ChevauchementActiviteException( String.valueOf(act.getDateDebut().getTimeInMillis()) );
				
			}
		}
		listeDesActivites.put(donnees.getDateDebut(),donnees);
		sauvegarder();
	}
	
	/**
	 * methode pour modifer une activité
	 * @param cle date de début de l'activité à modifier
	 * @param donnees nouvelle activité modifié
	 */
	public void modifierUneActivite(GregorianCalendar cle, Activity donnees) throws ChevauchementActiviteException {
		listeDesActivites.remove(cle);
		ajouterUneActivite(donnees);
		sauvegarder();
	}
	
	/**
	 * methode pour supprimer une activité de l'agenda
	 * @param date date de début de l'activité
	 */
	public void supprimerUneActivite(GregorianCalendar date) {
		listeDesActivites.remove(date);
		sauvegarder();
	}

	/**
	 * methode pour récupérer une activité
	 * @param cle la date de début de l'activité
	 * @return l'activité qui possède cle en date de debut
	 */
	public Activity getActivity(GregorianCalendar cle) throws ActivityNotFoundException {
		Activity retour = listeDesActivites.get(cle);
		if(retour == null){
			throw new ActivityNotFoundException();
		}
		return retour;
	}
	
	/**
	 * methode pour vérifier l'existance d'une activité
	 * @param cle date de commencement de l'activite
	 */
	public boolean existeActivity(GregorianCalendar cle) {
		return listeDesActivites.containsKey(cle);
	}
	
	/**
	 * Renvoie le nombre total d'activité dans l'agenda
	 * @return nombre total d'activité
	 */
	public int taille() {
		return listeDesActivites.size();
	}
	
	/**
	 * donne la liste des activités comprises entre [dateDebut;DateFin]
	 * @param dateDebut
	 * @param dateFin
	 */
	public ArrayList<Activity> getListeActivity(GregorianCalendar dateDebut,GregorianCalendar dateFin){

		ArrayList<Activity> ret = new ArrayList<Activity>();
		
		Enumeration it = listeDesActivites.keys();
		while(it.hasMoreElements()){
			Activity act = listeDesActivites.get(it.nextElement());
			GregorianCalendar dateDebut2 = act.getDateDebut();
			GregorianCalendar dateFin2 = act.getDateFin();
			// on regarde si la date de début dans la période demandé
			if( dateDebut.getTimeInMillis() <= dateDebut2.getTimeInMillis() && dateDebut2.getTimeInMillis() < dateFin.getTimeInMillis() ){
				ret.add(act);
			}
			// on regarde si la date de fin dans la période demandé
			else if( dateDebut.getTimeInMillis() < dateFin2.getTimeInMillis() && dateFin2.getTimeInMillis() <= dateFin.getTimeInMillis() ){
				ret.add(act);
			}
			// on regarde si l'activité ne se passe pas pendant la période demandé
			else if(dateDebut2.getTimeInMillis() < dateDebut.getTimeInMillis() && dateDebut.getTimeInMillis() < dateFin2.getTimeInMillis()){
				ret.add(act);
			}
		}
		
		return ret;
	}
	
	/**
	 * méthode pour remettre à 0 l'agenda
	 */
	public void reset(){
		listeDesActivites = new Hashtable<GregorianCalendar,Activity>();
	}
	
	/**
	 * Méthode responsable de l'écriture de l'objet AgendaEngine courant (this) dans un fichier "dataAgenda.bin"
	 */
	private void sauvegarder() {
		FileOutputStream out = null;
		ObjectOutputStream fluxSortie = null;
		try{
			// préparation de l'écriture dans dataAgenda.bin
			try{
				out = new FileOutputStream("../data/dataAgenda.bin");
			}
			catch(IOException e){
				System.out.print("Error : impossible d'écrire dans dataAgenda.bin "+e.getMessage());
			}
			// initialisation du flux d'écriture
			try{
				fluxSortie = new ObjectOutputStream(out);
			}
			catch(IOException e){
				System.out.print("Error : impossible d'initialiser le flux d'écriture "+e.getMessage());
			}
			// écriture de tabFiches dans le fichier dataAgenda.bin
			fluxSortie.writeObject(listeDesActivites);
		}
		catch(NotSerializableException e){
			System.out.print("Error : objet non serealisable");
		}
		catch(FileNotFoundException e){
			System.out.print("Error : le fichier ../data/dataAgenda.bin n'a pas été trouvé");
		}
		catch(IOException e){
			System.out.print("Error lors de la manipulation du flux ");
		}
		finally{
			// fermeture du flux
			try{
				fluxSortie.close();
			}
			catch(IOException e){}
		}
	}
	
	/**
	 * Méthode responsable de la lecture d'un objet AgendaEngine à partir du fichier "dataAgenda.bin"
	 */
	private void charger() {
		FileInputStream out = null;
		ObjectInputStream fluxEntree = null;
		try{
			// ouverture du fichier dataAgenda.bin
			try{
				out = new FileInputStream("../data/dataAgenda.bin");
			}
			catch(IOException e){
				System.out.print("Error : connexion au fichier dataAgenda.bin impossible "+e.getMessage());
			}
			
			// initialisation du flux de lecture
			try{
				fluxEntree = new ObjectInputStream(out);
			}
			catch(IOException e){
				System.out.print("Error : impossible d'initilaliser le flux de lecture "+e.getMessage());
			}
			
			// récupération des donnees sauvegardées
			listeDesActivites = (Hashtable<GregorianCalendar,Activity>)fluxEntree.readObject();
		}
		catch(FileNotFoundException e){
			System.out.print("Error : le fichier ../data/dataAgenda.bin n'a pas été trouvé");
		}
		catch(IOException e){
			System.out.print("Error : lors de l'utilisation du flux");
		}
		catch(ClassNotFoundException e){
			System.out.print("Error : la classe contenant l'objet listeDesActivites n'a pas été trouvé");
		}
		finally{
			// fermeture du flux
			try{
				fluxEntree.close();
			}
			catch(IOException e){}
		}
	}
}

